package org.wevil.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.TreeMap;

/**
 * 通联支付工具类
 * <p>开发文档地址：<a href="https://aipboss.allinpay.com/know/devhelp/main.php?pid=38#mid=313">通联支付开发者中心 </a></p>
 */
@Slf4j
public class AllinPayUtil {

    /**
     * 交易提交接口
     */
    public static String url_order_submit = "https://syb.allinpay.com/apiweb/h5unionpay/unionorder";
    /**
     * 交易查询接口
     */
    public static String url_order_query = "https://vsp.allinpay.com/apiweb/tranx/query";
    /**
     * 交易退款接口
     */
    public static String url_order_refund = "https://vsp.allinpay.com/apiweb/tranx/refund";
    /**
     * 交易关闭接口
     */
    public static String url_order_close = "https://vsp.allinpay.com/apiweb/tranx/close";
    /**
     * 通联RSA2生产公钥
     * */
    public static String rsa_pub = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB";

    /**
     * 商户私钥
     * */
    public static String pri_rsa = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClZU/QDwbR0lGvM8fe32W3Q05PXZ+2q6rSc7UlhPD1jGdVKpb7arn+L1KEgXxArZOS8ARWwQeVxIhCayC9JAKwiJMSYlQo3TePL+gurOkxIAaFM+Fm4Ersa2Zq8FhE/Eqre9LKtsYqoas+UqBhqNHtpS0he6QEK1pyClj8StO7YSsfzKlCn/YlAf6cDi0jMmEz5gJ/8inTnHGpc8yXnUahr1XVNWnDknLYazIYOYRj0ngUGjsY/B45qQfHU064x55bNVSjytiLrkNzHUk28G3kB75iO/eFl06wcadLEEKq02l/5gvTfr1YwY9bL8gnvIBUBryHkLYf5Gc8DdLZ4/PfAgMBAAECggEAOW1C2AfPo1y2NfHxrOW+KHJ7Jz1nrgJO7oF0XRGapHdajYybHbKB38rIiO94l/Bss/EYYJLnMk4pzX3qwSFWubrB7fksJYAhwVqt8D7jPMZU3T0VPyYTfaEQz4Kh81nyBzaYCwCJL9YwYhRIUYBPW4clc2G9oiD3Z29GVFsaxw03q+0A//lC+eirEnOXZXsEdi5Hf+sBNY/DgZBYmng98bOkXf4sPsPPZ9umpr6N9ZdyBEheGYOdYYEnV6mR6HRcTW+U33iqPAF5/SRWsVBn8hLcjkBJ8WTQBXK+g7dIIeIJ8q5dIo+0ZY8dC08eKoud9KtDynm2ggaCRK1fupe9UQKBgQDdG8PVlQRSc899HVjMxhQ7f79LAJjoLexL/CNiEqNLTGuEU2esn6yFruRQIKNJ6cySMU5OT8KkcwGkg1tbQlteDXaPrqcUbqxXJp0McuXmzs9qFoPE3KMPaOpDOlpwQGWcWXIY8Jeik1AmWS3DY4WQlG4aYAZyqvTDLeJ3u6c2aQKBgQC/fuQhGlEBczZXp2HMwmNZLsg6tQphG50fSH2mzOx6DooCET0UgrQemYLweb0rQN35pPu2OTll6vx5rZku/M3q5NZyncC18REzcyoYizcCIgwqNQ1N1TzodUZupYf6kOzoq+lTMIUr4UU8HU97j9jVZaXWCErbba3yepqBCwzfBwKBgFLw/103MQ/c8ZB1WcI265AGM7Pm3XT7BRDb3EoFw8uTNyHER7exjf33xR1poNcxJNQSOtWVheY6Xftgavyov61gT4FGetZnTa7DhVYOI7+ZBKU/HN7UBTbanEXrtdE11uf3hZ1lYvxRVleMTGZnBstdYo/yRXjuODKfG7zeX7NBAoGACh2j43PGb7MsXbkD25MxOFbWUPObVsHkrPBhYtxUaaY2+CD8/wZgiXVGi3mC2rQUhcmHWnrvY0jkiXD4NJk6L3xjL67RrUrMw9EDmkRQZZZy2g/vYfsvun/ndpTb+AqL2Uarjq9zeXvu8lwCBN72b19tQT8y5SCIHdUIMGTrCyUCgYEAri2SFB5ATkuxkCc5ZGU0VTGJ2r7DCYwYo33QX/KpnJnA9qmoQqzJK+wkpiog1x0TIAnJZXmq3kGYjTW1Ddt1tGDzyuLHR7p0SNrVWkctrIvLJJ43mbM+jhX1VrfRki1zxu3vOfKa4Rk6lvRgTHBHmxLbo8I8cjTBjB9QC5OB0sc=";

    /**
     * 商户公钥
     * */
    public static String pub_rsa = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA83wIl/qJ4HAe86KzakPUA2OXs8jc1o76JASNCRIbJwsvuecnjAzms5TYi+zTnT5lsLSCpfLHSOU2oHSPlJ8FckHg4MQ8y5z9N/aSFLiLehKZUF0lECu/9o9ZpXxljCKnAzrSj6vKdY2pshRnLK9xtPx1k/a701A8kkEFlbXZwsL6WKdRSc4hkuTeDWSMxEAkx5Pfnsg7qLlYf6cYYqLZQMyeUoVDutjaGGkWHvPQ6gCD8WsSlTD32ZH50c+SLq7a9ygiwkGlhV7YKsoEMuRW/yLSfW0qbnWdkMDzBJXY8NRKQa+urNd4/2YMDYBRj7a8XEfPYUtZMmncoNLFRRX8fwIDAQAB";

    /**
     * 根据参数生成签名
     */
    public static String sign(Map<String, String> params, String privateKey) throws Exception {
        String sb = map2param(params);
        return rsaSign(sb, privateKey, "UTF-8");
    }

    /**
     * 生成请求参数，拼接在url后面
     */
    public static String unionSign(TreeMap<String, String> params, String privateKey) throws Exception {
        StringBuilder sb = new StringBuilder();
        StringBuilder sbEncode = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                String encodeValue = URLEncoder.encode(entry.getValue(), "UTF-8");
                sbEncode.append(entry.getKey()).append("=").append(encodeValue).append("&");
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        if (sbEncode.length() > 0) {
            sbEncode.deleteCharAt(sbEncode.length() - 1);
            sb.deleteCharAt(sb.length() - 1);
        }
        String sign = rsaSign(sb.toString(), privateKey, params.get("charset"));
        String signEncoded = URLEncoder.encode(sign, "utf-8");
        sbEncode.append("&sign=").append(signEncoded);
        return sbEncode.toString();
    }

    /**
     * 动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常
     */
    public static TreeMap<String, String> getParams(HttpServletRequest request) {
        TreeMap<String, String> map = new TreeMap<>();
        Map<String, String[]> reqMap = request.getParameterMap();
        for (String key : reqMap.keySet()) {
            String value = reqMap.get(key)[0];
            System.out.println(key + ";" + value);
            map.put(key, value);
        }
        return map;
    }

    /**
     * 验签
     */
    public static boolean validSign(Map<String, String> params) throws Exception {
        if (params != null && !params.isEmpty()) {
            if (!params.containsKey("sign")){
                return false;
            }
            String sign = params.remove("sign");
            String sb = map2param(params);
            return rsaVerifyPublicKey(sb, sign);
        }
        return false;
    }

    private static String map2param(Map<String, String> param){
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : param.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    private static boolean rsaVerifyPublicKey(String content, String sign,
                                             PublicKey pubKey) throws Exception {
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initVerify(pubKey);
        signature.update(content.getBytes(StandardCharsets.UTF_8));
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    private static boolean rsaVerifyPublicKey(String content, String sign) throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509(Base64.decodeBase64(rsa_pub.getBytes()));
            return rsaVerifyPublicKey(content, sign, pubKey);
        } catch (Exception e) {
            throw new Exception("RSAContent = " + content + ",sign=" + sign + ",charset = " + "UTF-8", e);
        }
    }

    private static PublicKey getPublicKeyFromX509(byte[] encodedKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

    private static String rsaSign(String content, String privateKey,
                                  String charset) throws Exception {
        PrivateKey priKey = getPrivateKeyFromPKCS8(
                Base64.decodeBase64(privateKey.getBytes()));
        return rsaSign(content, priKey, charset);
    }

    private static PrivateKey getPrivateKeyFromPKCS8(byte[] encodedKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    private static String rsaSign(String content, PrivateKey priKey,
                                  String charset) throws Exception {
        java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
        signature.initSign(priKey);
        if (charset == null || charset.isEmpty()) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }
        byte[] signed = signature.sign();
        return new String(Base64.encodeBase64(signed));
    }

    // -------------------------------------测试--------------------------------------------------//

    public static void main(String[] args) throws Exception {
        Map<String, String> params = new TreeMap<>();
        params.put("appid", "00308262");
        params.put("body", "iPhone15");
        params.put("cusid", "563881053992LU7");
        params.put("notify_url", "https://dev.xjzdxxkj.com/allinpay/notify");
        params.put("paytype", "W06");
        params.put("randomstr", "1081978782629");
        params.put("reqsn", "1406179657236");
        params.put("signtype", "RSA");
        params.put("trxamt", "1");
        params.put("version", "12");
//        params.put("remark", "White 256GB");
        String sign = sign(params, pri_rsa);
        System.out.println(sign);
    }
}
